﻿using System;
using System.Collections.Generic;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using Microsoft.Win32;
using PredatorPrey.GUI.Items;
using PredatorPrey.Library;
using PredatorPrey.Library.Algorithms;

namespace PredatorPrey.GUI
{
	// Id		: $Id: Main.xaml.cs 39 2009-04-23 21:38:07Z kristof@kristofmattei.be $
	// Date		: $Date: 2009-04-23 21:38:07 +0000 (Thu, 23 Apr 2009) $
	// Author	: $Author: kristof@kristofmattei.be $
	// Revision	: $Revision: 39 $
	/// <summary>
	/// Interaction logic for Main.xaml
	/// </summary>
	public partial class Main : Window
	{
		/// <summary>
		/// 
		/// </summary>
		public Field Field { get; set; }

		/// <summary>
		/// 
		/// </summary>
		//private Items.Block predator;

		/// <summary>
		/// 
		/// </summary>
		//private Items.Block prey;

		/// <summary>
		/// 
		/// </summary>
		private AStarSearch aStarSearch;

		/// <summary>
		/// The constructor, which initialiazes the UI
		/// Displays the 'map' open dialog
		/// And draws the field
		/// </summary>
		public Main()
		{
			this.InitializeComponent();
			
			OpenFileDialog openFileDialog = new OpenFileDialog()
			{
				CheckFileExists = true,
				Filter = "Map files|*.txt",
				Title = "Select a map"
			};

			if (openFileDialog.ShowDialog() != true)
			{
				//finish nice and easy
				Environment.Exit(0);
			}

			this.Field = this.ReadFile(openFileDialog.FileName);

			this.aStarSearch = new AStarSearch(this.Field);

			this.DrawField();
		}

		/// <summary>
		/// Draws the field to the screen
		/// </summary>
		private void DrawField()
		{
			for (int x = 0; x < this.Field.Width; x++)
			{
				this.mainGrid.RowDefinitions.Add(new RowDefinition());
			}

			for (int y = 0; y < this.Field.Height; y++)
			{
				this.mainGrid.ColumnDefinitions.Add(new ColumnDefinition());
			}

			List<Block> blocks = new List<Block>();

			foreach (Cell cell in this.Field)
			{
				//TODO: REMOVE THIS. Predator and Prey will be moved out of the field
				if (cell.FieldType == FieldType.Predator)
				{
					//this.predator = new Items.Block();

					//this.predator.Child = new Image() 
					//{ 
					//	Source = new BitmapImage(new Uri("Images/Wilecoyote.png", UriKind.Relative)) 
					//};

					Grid.SetRow(predator, cell.Y);
					Grid.SetColumn(predator, cell.X);

					//this.mainGrid.Children.Add(predator);

					//this.DrawPredatorAndPrey(cell);
				}
				else if (cell.FieldType == FieldType.Prey)
				{
					//this.prey = new Items.Block();
					//this.prey.Child = new Image() 
					//{ 
					//    Source = new BitmapImage(new Uri("Images/Roadrunner.png", UriKind.Relative)) 
					//};

					//this.mainGrid.Children.Add(prey);

					//this.DrawPredatorAndPrey(cell);

					Grid.SetRow(prey, cell.Y);
					Grid.SetColumn(prey, cell.X);
				}
				else
				{
					Items.Block block = new Items.Block();
					blocks.Add(block);

					this.mainGrid.Children.Add(block);

					switch (cell.FieldType)
					{
						case FieldType.Grass:
							block.Background = Brushes.Green;
							break;

						case FieldType.Swamp:
							block.Background = Brushes.Brown;
							break;

						case FieldType.Wall:
							block.Background = Brushes.Black;
							break;
					}

					Grid.SetRow(block, cell.Y);
					Grid.SetColumn(block, cell.X);
				}
			}
		}

		/// <summary>
		/// Draws the predator and the prey to the screen.
		/// </summary>
		/// <param name="cell">The cell where the predator/prey is</param>
		//public void DrawPredatorAndPrey(Cell cell)
		//{
		//    Items.Block block;

		//    if (cell.FieldType == FieldType.Predator)
		//    {
		//        block = this.predator;
		//    }
		//    else if (cell.FieldType == FieldType.Prey)
		//    {
		//        block = this.prey;
		//    }
		//    else
		//    {
		//        throw new ArgumentException("cell");
		//    }

		//    Grid.SetRow(block, cell.Y);
		//    Grid.SetColumn(block, cell.X);
		//}

		/// <summary>
		/// Draws the path to the screen
		/// </summary>
		private void PlotPath()
		{
			Timer timer = new Timer(500);

			Cell start = this.Field.SearchFor(FieldType.Predator);
			Cell goal = this.Field.SearchFor(FieldType.Prey);
				
			List<Cell> path = this.aStarSearch.SearchPath(start, goal);

			int index = 0;

			timer.Elapsed += (o, e) =>
			{
				if (index == path.Count - 1)
					timer.Stop();
#if DEBUG
				Console.WriteLine(path[index]);
#endif

				//this.Dispatcher.Invoke((Action)delegate
				//{
				//    Grid.SetRow(this.predator, path[index].Y);
				//    Grid.SetColumn(this.predator, path[index].X);
				//}, new object[] { });

				//I like this way better, without the reflection
				this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate
				{
					Grid.SetRow(this.predator, path[index].Y);
					Grid.SetColumn(this.predator, path[index].X);
				});

				//move one step ahead on the path
				index++;
			};

			timer.Start();
		}

		/// <summary>
		/// Reads a map file
		/// </summary>
		/// <param name="fileName">The map location</param>
		/// <returns>A field</returns>
		private Field ReadFile(string fileName)
		{
			List<string> lines = new List<string>();

			//read the file
			//we need to change this to a filedialog
			System.IO.StreamReader file = new System.IO.StreamReader(fileName);

			string line = string.Empty;

			while ((line = file.ReadLine()) != null)
			{
				lines.Add(line);
			}

			//we now have the grid in a very stupid way.
			//convert it and pass it on

			return this.ConvertToField(lines);
		}

		/// <summary>
		/// Convert a given set of lines to a Field
		/// </summary>
		/// <param name="lines">The lines from a text file</param>
		/// <returns>a Field</returns>
		private Field ConvertToField(List<string> lines)
		{
			if (lines == null)
			{
				throw new ArgumentNullException("Lines cannot be null");
			}

			//get the width
			int width = lines[0].Length;

			//get the height
			int height = lines.Count;

			Field field = new Field(width, height);

			//loop over the lines
			//x and y are swapped because that is how you access a list, first the y and then the x
			for (int x = 0; x < width; x++)
			{
				for (int y = 0; y < height; y++)
				{
					field[x, y] = new Cell(x, y, this.ConvertCharToFieldType(lines[y][x]));
				}
			}

			//we really don't need this anymore
#if DEBUG
			//this.Visualize(field);
#endif
			return field;
		}

		/// <summary>
		/// Converts a char (from a file) to an FieldType
		/// </summary>
		/// <param name="c">The char</param>
		/// <returns>An equivilent FieldType for the char</returns>
		private FieldType ConvertCharToFieldType(char c)
		{
			FieldType fieldType;

			switch (c)
			{
				case 'W':
					fieldType = FieldType.Wall;
					break;
				case 'G':
					fieldType = FieldType.Grass;
					break;
				case 'E':
					fieldType = FieldType.Empty;
					break;
				case 'S':
					fieldType = FieldType.Swamp;
					break;
				case 'Y':
					fieldType = FieldType.Prey;
					break;
				case 'P':
					fieldType = FieldType.Predator;
					break;
				default:
					throw new ArgumentOutOfRangeException("char is not in accepted range");
			}

			return fieldType;
		}

		/// <summary>
		/// Stupid method to display the Field
		/// </summary>
		/// <param name="Field">An initialized Field</param>
		//private void Visualize(Field field)
		//{
		//    if (field == null)
		//    {
		//        throw new ArgumentNullException("Field cannot be null");
		//    }

		//    string output = string.Empty;

		//    for (int y = 0; y < field.Height; y++)
		//    {
		//        for (int x = 0; x < field.Width; x++)
		//        {
		//            output += "+----------";
		//        }

		//        output += "+\n";

		//        for (int x = 0; x < field.Width; x++)
		//        {
		//            output += string.Format("| {0,-9}", field[x, y].FieldType);
		//        }

		//        output += "|\n";
		//    }

		//    for (int x = 0; x < field.Width; x++)
		//    {
		//        output += "+----------";
		//    }

		//    output += "+\n";

		//    Console.WriteLine(output);
		//}

		/// <summary>
		/// Handles the button click
		/// </summary>
		private void searchButton_Click(object sender, RoutedEventArgs e)
		{
			this.PlotPath();
		}
	}
}